import pandas as pd

from SystematicFactors.General import *
import Option.OptionGadget

# Calc_FutureSpotBasis 20240426 进行迭代
# 还有一个版本在 test_calculate_basis
# 原版因子名称
# IC_FutSpotBasis_Weighted
# IF_FutSpotBasis_Weighted
# IH_FutSpotBasis_Weighted
# 计划调整为：
# IF_Basis_Time_Weighted # 到期日加权
# IF_Basis_Vol_Weighted # 交易量加权
# 参考 Basis v2
def Calc_FutureSpotBasis(database, datetime1, datetime2, code):
    # 计算加权基差
    def Calc_VolumeWeighted(row):
        columns = row.index
        totalVolume = 0
        #
        value = 0
        for col in columns:
            if "Basis" in col:
                x = col.split("_")
                symbol = x[0]
                volume = row[symbol + "_Volume"]
                basis = row[symbol + "_Basis"]

                # 如果最后一天到期日，基差是0/NA
                if np.isnan(basis):
                    continue
                #
                totalVolume += volume
                value += volume * basis
        # 加权处理
        value = value / totalVolume
        #
        # print(row["Date"], value)
        return value

    # 每个合约计算年化基差
    def CalcFutBasis(df_Spot, symbol, settlementDay, datetime1, datetime2):
        #
        filter = {"Symbol": symbol}
        filter["Date"] = {">=": datetime1.date(), "<=": datetime2.date()}
        documents = database.Find("DailyBar", "Future", filter)
        if len(documents) == 0:
            return pd.DataFrame
        #
        df_Fut = Gadget.DocumentsToDataFrame(documents,
                                             keep=["date", "close", "volume"],
                                             rename={"close": "Fut_Close", "date":"Date", "volume":"Volume"})
        df_Fut = pd.merge(df_Fut, df_Spot, how="left", on="Date")
        #
        df_Fut["DayLeft"] = (settlementDay.date() - df_Fut["Date"]).dt.days
        df_Fut["AnnualizedFactor"] = 365 / df_Fut["DayLeft"]
        # print(df_Fut)

        # 最后一天无法计算基差时候当NA处理,不要抛弃
        df_Fut.replace([np.inf, -np.inf], np.nan, inplace=True)
        # df_Fut.dropna(0, inplace=True)

        #
        df_Fut["Basis"] = df_Fut["Fut_Close"] / df_Fut["Spot_Close"] - 1
        df_Fut["AnnualBasis"] = df_Fut["Basis"] * df_Fut["AnnualizedFactor"]
        #
        return df_Fut

    def Update_TotalFutBasis(df_Spot, code, lastSettlementDay):
        # print("Update_TotalFutBasis", lastSettlementDay, nextSettlementDay)
        df = pd.DataFrame()
        terms = Option.OptionGadget.Get4Terms(lastSettlementDay, 3, 5)

        nextSettlementDay = None
        for i in range(len(terms)):
            t = terms[i]
            # Load Symbol SettlementDay
            symbol = Option.OptionGadget.term_to_symbol(code, t, "CFE")
            instruments = database.Find("Instruments", "Future", {"Symbol": symbol})
            if len(instruments) == 0:
                continue
            instrument = instruments[0]
            settlementDay = instrument["datetime2"]
            if nextSettlementDay == None:
                nextSettlementDay = settlementDay
                print("Update_TotalFutBasis", lastSettlementDay, nextSettlementDay)

            # 读取合约
            df_Fut = CalcFutBasis(df_Spot, symbol, settlementDay, lastSettlementDay, nextSettlementDay)
            if df_Fut.empty:
                print("No Data for CalcFutBasis")
                break
            # print(symbol)
            # print(df_Fut)
            df_Fut = df_Fut[["Date","AnnualBasis", "Volume"]]
            df_Fut.rename(columns={"AnnualBasis": symbol + "_Basis", "Volume": symbol + "_Volume"}, inplace=True)
            #
            if df.empty:
                df = df_Fut
            else:
                df = pd.merge(df, df_Fut, how="left", on="Date")
        #
        if not df.empty:
            df.dropna(inplace=True)
            df["Weighted"] = df.apply(Calc_VolumeWeighted, axis=1)
            # df.to_csv("d:/data/timingModel/FutBasis_" + Gadget.ToDateString(nextSettlementDay) + ".csv")
        # print(df)
        return nextSettlementDay, df

    #
    if code == "IF":
        spotSymbol = "000300.SH"
    elif code == "IC":
        spotSymbol = "000905.SH"
    elif code == "IH":
        spotSymbol = "000016.SH"

    documents = database.Find("DailyBar", "Index", {"Symbol": spotSymbol})
    df_Spot = Gadget.DocumentsToDataFrame(documents,
                                          keep=["date", "close"],
                                          rename={"close": "Spot_Close", "date":"Date"})
    # print(df_Spot)
    df_future_bar = database.Get_Daily_Bar_DataFrame(symbol=code + ".CFE", instrument_type="Future",
                                                     projection=["date", "close"])
    start_date = df_future_bar.iloc[0]["date"]
    start_datetime = datetime.datetime(start_date.year, start_date.month, start_date.day)
    #
    if datetime1 < start_datetime:
        realtime_datetime1 = start_datetime
    else:
        realtime_datetime1 = datetime1
    #
    changeMonth = True
    curDateTime = realtime_datetime1
    nextSettlementDay = realtime_datetime1
    dfs = []
    while curDateTime < datetime2:
        #
        # print(curDateTime)
        if curDateTime == nextSettlementDay:
            changeMonth = True
        #
        if changeMonth:
            nextSettlementDay, df = Update_TotalFutBasis(df_Spot, code, nextSettlementDay)
            print(df)
            if not df.empty:
                dfs.append(df[["Date", "Weighted"]])
            changeMonth = False
            nextSettlementDay += datetime.timedelta(days=1)

        curDateTime += datetime.timedelta(days=1)
    #
    df = pd.concat(dfs)
    # print(df)
    df.rename(columns={"Weighted": code + "_FutSpotBasis_Weighted"}, inplace=True)
    df.set_index(df["Date"], drop=True, inplace=True)
    print(df)
    Save_Systematic_Factor_To_Database(database, df, code + '_FutSpotBasis_Weighted')


if __name__ == '__main__':
    #
    # import os
    # pathfilename = os.getcwd() + "\..\Config\config2.json"
    # config = Config.Config(pathfilename)
    # database = config.DataBase("JDMySQL")

    path_filename = os.getcwd() + "\..\Config\config_local.json"
    database = Config.create_database(database_type="MySQL", config_file=path_filename, config_field="MySQL")

    w.start()

    datetime1 = datetime.datetime(2020, 7, 1)
    datetime2 = datetime.datetime(2020, 8, 28)
    Calc_FutureSpotBasis(database, datetime1, datetime2, "IH")
    Calc_FutureSpotBasis(database, datetime1, datetime2, "IC")
    Calc_FutureSpotBasis(database, datetime1, datetime2, "IF")